package zombiegame;


import graphics.Field;
import graphics.Location;
import graphics.Randomizer;
import graphics.SimulatorView;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

/**
 * Simulator for Midterm Zombiegame.
 * @author pylaffon
 *
 */
public class Simulator {
    // Default health points  for our creatures

    public static final int  HP_HUMANS = 100;
    public static final int  HP_VAMPIRES = 150;
    static final int  HP_ZOMBIES = 130;
    private static final double HUMAN_CREATION_PROBABILITY = 0.10;
    // The probability that a rabbit will be created in any given grid position.
    private static final double VAMPIRE_CREATION_PROBABILITY = 0.05;
    private static final double ZOMBIE_CREATION_PROBABILITY = 0.03;
    // The default width for the grid.
    private static final int DEFAULT_WIDTH = 50;
    // The default depth of the grid.
    private static final int DEFAULT_DEPTH = 50;



    // Lists of animals in the field. Separate lists are kept for ease of
    // iteration.
    private List<Vampire> vampires;
    private List<Human> humans;
    private List<Zombie> zombies;


    // A graphical view of the simulation.
    private static SimulatorView view;
    private static Field field = new Field(DEFAULT_DEPTH, DEFAULT_WIDTH);
    // The current step of the simulation.
    private int step;


    /**
     * Construct a simulation field with default size.
     */
    public Simulator() {
        this(DEFAULT_DEPTH, DEFAULT_WIDTH);
    }


    /**
     * @return the number of human characters currently in the game
     */
    public int nbHumansAlive() {
        //Need to iterate through the list of characters
        //and count the number of humans
        int nbHumans = humans.size();
        return nbHumans;
    }

    /**
     * Generate a pseudo-random boolean.
     * @return pseudo-random boolean
     */
    public static boolean GenerateRandomBoolean() {
        Random random = new Random();
        return random.nextBoolean();
    }

    /**
     * Create a simulation field with the given size.
     * 
     * @param depth
     *            Depth of the field. Must be greater than zero.
     * @param width
     *            Width of the field. Must be greater than zero.
     */
    public Simulator(int depth, int width) {
        if (width <= 0 || depth <= 0) {
            System.out.println("The dimensions must be greater than zero.");
            System.out.println("Using default values.");
            depth = DEFAULT_DEPTH;
            width = DEFAULT_WIDTH;
        }

        humans = new ArrayList<Human>();
        vampires = new ArrayList<Vampire>();
        zombies = new ArrayList<Zombie>();

        field = new Field(depth, width);

        // Create a view of the state of each location in the field.
        view = new SimulatorView(depth, width);
        view.setColor(Human.class, Color.orange);
        view.setColor(Vampire.class, Color.red);
        view.setColor(Zombie.class, Color.green);

        // Setup a valid starting point.
        reset();
    }

    /**
     * Reset the simulation to a starting position.
     */
    public void reset() {
        step = 0;
        vampires.clear();
        humans.clear();
        zombies.clear();
        populate();

        // Show the starting state in the view.
        view.showStatus(step, field);
    }

    /**
     * Randomly populate the field with foxes and rabbits.
     */
    private void populate() {
        Random rand = Randomizer.getRandom();
        field.clear();
        for (int row = 0; row < field.getDepth(); row++) {
            for (int col = 0; col < field.getWidth(); col++) {
                if (rand.nextDouble() <= ZOMBIE_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Zombie zombie = new Zombie("Zombie" + col, HP_ZOMBIES, field, location);
                    zombies.add(zombie);
                } else if (rand.nextDouble() <= VAMPIRE_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Vampire vampire = new Vampire("Vampire" + col, HP_VAMPIRES, field, location);
                    vampires.add(vampire);
                } else if (rand.nextDouble() <= HUMAN_CREATION_PROBABILITY) {
                    Location location = new Location(row, col);
                    Human human = new Human("Human" + col, HP_HUMANS, field, location);
                    humans.add(human);
                }
                // else leave the location empty.
            }
        }
    }
    
    
    /**
     * Run the simulation from its current state for the given number of steps.
     * Stop before the given number of steps if it ceases to be viable.
     * 
     * @param numSteps
     *            The number of steps to run for.
     */
    public void simulate(int numSteps) {
        for (int step = 1; step <= numSteps && view.isViable(field); step++) {
            simulateOneStep();
            try {
                Thread.sleep(100); //temporisation de 500 ms pour voir l'ordre dans lequel les segments sont dessin�s
            } catch (InterruptedException e) {
                e.printStackTrace();
            } 
        }
    }

    /**
     * Run the simulation from its current state for a single step. Iterate over
     * the whole field updating the state of each fox and rabbit.
     */
    public void simulateOneStep() {
        step++;

       
        // Let all zombies act.
        for (Iterator<Zombie> it = zombies.iterator(); it.hasNext();) {
            Zombie zombie = it.next();
            if (!zombie.isAlive()) {
                it.remove();
            } else {
                zombie.endOfTurn();
            }
        }
        
        // Provide space for newborn humans.
        List<Human> newHumans = new ArrayList<Human>();
        List<Vampire> newVampires = new ArrayList<Vampire>();

        // Let all rabbits act.
        for (Iterator<Human> it = humans.iterator(); it.hasNext();) {
            Human human = it.next();
            
            if (!human.isAlive()) {
                it.remove();
            } else {
                human.endOfTurn(newHumans, newVampires);
            }
        }
        
        // Let all vampires act.
        for (Iterator<Vampire> it = vampires.iterator(); it.hasNext();) {
            Vampire vampire = it.next();
            if (!vampire.isAlive()) {
                it.remove();
            } else {
                vampire.endOfTurn();
            }
        }


        // Add the newly born foxes and rabbits to the main lists.
            humans.addAll(newHumans);
            vampires.addAll(newVampires);


        view.showStatus(step, field);
    }
}
